﻿#region Copyright ©2008-2009, Technische Universitaet Muenchen
// ====================================================================================================
//
// Last Changed by       $Author: then $
// Last Changed Date     $LastChangedDate: 2009-06-15 08:45:45 +0200 (Mon, 15 Jun 2009) $
// Last Changed Revision $Rev: 194 $
//
// ====================================================================================================
#endregion

using System;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace Tum.CollabXT.Wizard
{
    /// <summary>
    /// The wizard's conversion workflow.
    /// </summary>
    internal class ConversionWorkflow : IConversionWorkflow
    {
        /// <summary>
        /// The workflow pages host frame.
        /// </summary>
        private Frame HostFrame;

        /// <summary>
        /// Back button.
        /// </summary>
        private Button ButtonBack;

        /// <summary>
        /// Next button.
        /// </summary>
        private Button ButtonNext;


        /// <summary>
        /// True, if a valid processing has been created, else if not or if it has been invalidated since then.
        /// </summary>
        internal bool ValidProcessingExists
        {
            get { return _ValidProcessingExists; }
        }
        bool _ValidProcessingExists = false;

        /// <summary>
        /// Loggin class.
        /// </summary>
        public Log Log
        {
            get { return _Log; }
        }
        private Log _Log;

        /// <summary>
        /// Current conversion project.
        /// </summary>
        public ConversionProject Project
        {
            get { return _Project; }
            set { _Project = value; }
        }
        ConversionProject _Project;


        /// <summary>
        /// Gets the current workflow step's title
        /// </summary>
        public string Title
        {
            get { return _Title; }
        }
        string _Title;

        /// <summary>
        /// Delegate type for the event WorkflowTitleChanged.
        /// </summary>
        /// <param name="newTitle">New workflow title.</param>
        public delegate void WorkflowTitleChangedDelegate(string newTitle);

        /// <summary>
        /// Event that signals a change in the current workflow title.
        /// </summary>
        public event WorkflowTitleChangedDelegate WorkflowTitleChanged;


        /// <summary>
        /// Gets the plugin manager
        /// </summary>
        public PluginManager PluginMgr
        {
            get { return _PluginMgr; }
        }
        private PluginManager _PluginMgr;


        /// <summary>
        /// taskId of the tool provider currently being configured.
        /// </summary>
        int CurrentToolProvider = -1;


        /// <summary>
        /// Argument of the last call of ChangePage
        /// </summary>
        bool LastPageChangeDirection = true;


        /// <summary>
        /// Current state within the workflow.
        /// </summary>
        private int CurWorkflowState = -1;

        /// <summary>
        /// Workflow steps.
        /// </summary>
        private readonly string[] Workflow = { "WizardWelcomePage.xaml", "SelectProcessProviderPage.xaml", "//ConfigProcessProvider", "//InitializeProcessProvider",
                                               "SelectToolProvidersPage.xaml", "//CheckIfProvidersAreConfiguredPre", "//ConfigToolProviders",
                                               "//CheckIfProvidersAreConfiguredAfter", "ProcessingPage.xaml", "FinishedPage.xaml" };


        /// <summary>
        /// Constructur.
        /// </summary>
        internal ConversionWorkflow(Frame workflowHostingFrame, Button buttonBack, Button buttonNext, Log log)
        {
            #region Save input values
            if (log == null)
                throw new CxtWizardException("Log parameter must not be null");

            if (workflowHostingFrame == null)
                throw new CxtWizardException("WorkflowHostingFrame parameter must not be null");

            if (buttonBack == null || buttonNext == null)
                throw new CxtWizardException("Button parameters must not be null.");

            this._Log = log;
            this.HostFrame = workflowHostingFrame;
            this.ButtonBack = buttonBack;
            this.ButtonNext = buttonNext;

            //Set events
            ButtonBack.Click += new RoutedEventHandler(ButtonBackClickHandler);
            ButtonNext.Click += new RoutedEventHandler(ButtonNextClickHandler);
            HostFrame.Navigated += new NavigatedEventHandler(WizardFrameNavigatedHandler);
            #endregion

            //Initialize plugin manager
            _Log.AddEntry("Loading plugins ...");
            _PluginMgr = new PluginManager();
            _Log.AddEntry("done.");

            NextPage();
        }

        /// <summary>
        /// Changes the current workflow step
        /// </summary>
        /// <param name="advance">If true, the next step is displayed, if false the previous one.</param>
        private void ChangeWorkflowStep(bool advance)
        {
            if (advance && CurWorkflowState + 1 >= Workflow.Length)
                return;
            if (!advance && CurWorkflowState == 0)
                return;

            //Save direction
            LastPageChangeDirection = advance;

            //Calculate next state
            CurWorkflowState += advance ? 1 : -1;
            UpdateButtonState(CurWorkflowState != 0, CurWorkflowState != Workflow.Length - 1);

            //Change page
            if (!Workflow[CurWorkflowState].StartsWith("//", StringComparison.OrdinalIgnoreCase))
                HostFrame.Source = new Uri("pack://application:,,,/" + Workflow[CurWorkflowState]);
            else
            {
                try
                {
                    switch (Workflow[CurWorkflowState])
                    {
                        case "//ConfigProcessProvider":
                            {
                                if (Project.ProcessProvider == null)
                                    throw new CxtWizardException("Process provider is null.");

                                string PPConfigPageName = Project.ProcessProvider.Instance.GetConfigPageName();

                                if (PPConfigPageName != null)
                                    HostFrame.Source = Helper.GetExternalPageUri(Project.ProcessProvider.AssemblyPath, PPConfigPageName);
                                else
                                    ChangeWorkflowStep(advance); //Skip this step
                            }
                            break;

                        case "//InitializeProcessProvider":
                            {
                                if (advance)
                                {
                                    InitializingProcessProvider ipp = new InitializingProcessProvider();
                                    ipp.Owner = HostFrame.Parent as Window;
                                    ipp.Workflow = this;
                                    bool? dialogRes = ipp.ShowDialog();
                                    ChangeWorkflowStep(dialogRes == true);
                                }
                                else
                                    ChangeWorkflowStep(advance);
                            }
                            break;

                        case "//ConfigToolProviders":
                            {
                                if (CurrentToolProvider >= Project.ToolProviders.Count)
                                    throw new CxtWizardException("No tool provider selected.");

                                if (!Project.ToolProviders[CurrentToolProvider].Initialized)
                                {
                                    Project.ToolProviders[CurrentToolProvider].Instance.Initialize(Project.ProcessProvider.Instance, this.Log);
                                    Project.ToolProviders[CurrentToolProvider].Initialized = true;
                                }

                                string TPConfigPageName = Project.ToolProviders[CurrentToolProvider].Instance.GetConfigPageName();
                                if (TPConfigPageName != null)
                                    HostFrame.Source = Helper.GetExternalPageUri(Project.ToolProviders[CurrentToolProvider].AssemblyPath, TPConfigPageName);
                                else
                                    ChangeWorkflowStep(advance); //Skip this step
                            }
                            break;

                        case "//CheckIfProvidersAreConfiguredPre":
                        case "//CheckIfProvidersAreConfiguredAfter":
                            {
                                bool preStep = Workflow[CurWorkflowState].EndsWith("Pre", StringComparison.OrdinalIgnoreCase);

                                if (advance)
                                {
                                    CurrentToolProvider++;

                                    if (CurrentToolProvider < Project.ToolProviders.Count)
                                        ChangeWorkflowStep(preStep);
                                    else
                                    {
                                        if (preStep)
                                            throw new CxtWizardException("No provider configured.");
                                        else
                                            ChangeWorkflowStep(true);
                                    }
                                }

                                if (!advance)
                                {
                                    CurrentToolProvider--;

                                    if (CurrentToolProvider >= 0)
                                        ChangeWorkflowStep(preStep);
                                    else
                                        ChangeWorkflowStep(false);
                                }
                            }
                            break;
                    }
                }
                catch (Exception e)
                {
                    Log.AddEntry("Can't change page.");
                    Log.AddEntry("Exception: " + e.Message);
                    Log.AddEntry("Source: " + e.Source);
                    Log.AddEntry("Stack trace: " + e.StackTrace);
                }
            }
        }

        /// <summary>
        /// Event handler: The current page has changed.
        /// </summary>
        private void WizardFrameNavigatedHandler(object sender, NavigationEventArgs e)
        {
            //Pass workflow handle to page
            (e.Content as ConversionWorkflowPage).ReceiveWorkflowHandle(this);

            #region Config process provider page
            if (e.Content is ProcessProviderConfigPage)
            {
                //Pass provider handle
                (e.Content as ProcessProviderConfigPage).ReceiveProviderHandle(Project.ProcessProvider.Instance);

                //Update workflow title
                _Title = String.Format(Wizard.Resources.Language.Title_ConfigProcessProviderPage, Project.ProcessProvider.Name);
            } 
            #endregion

            #region Config tool provider page
            if (e.Content is ToolProviderConfigPage)
            {
                //Pass provider handle
                (e.Content as ToolProviderConfigPage).ReceiveProviderHandle(Project.ToolProviders[CurrentToolProvider].Instance);

                //Update workflow title
                _Title = String.Format(Wizard.Resources.Language.Title_ConfigToolProviderPage, Project.ToolProviders[CurrentToolProvider].Name);
            } 
            #endregion

            #region For wizard pages only
            if(e.Content is InternalConversionWorkflowPage)
            {
                if (!(e.Content as InternalConversionWorkflowPage).InternalReceiveWorkflowHandle(this))
                {
                    //Skip page. This behavior is needed e.g. by the processing page.
                    ChangeWorkflowStep(LastPageChangeDirection);
                }

                //Update workflow title
                _Title = (e.Content as Page).Title;
            }
            #endregion

            //Notify title subscribers
            WorkflowTitleChanged(_Title);
        }

        /// <summary>
        /// Event handler: The "next" button has been clicked.
        /// </summary>
        private void ButtonNextClickHandler(object sender, RoutedEventArgs e)
        {
            //Check if advancing to next page is possible - if yes, do it.
            if((HostFrame.Content as ConversionWorkflowPage).NextPageRequested())
                NextPage();
        }

        /// <summary>
        /// Event handler: The "back" button has been clicked.
        /// </summary>
        private void ButtonBackClickHandler(object sender, RoutedEventArgs e)
        {
            //Check if going back one page is possible - if yes, do it.
            if ((HostFrame.Content as ConversionWorkflowPage).PrevPageRequested())
                PreviousPage();
        }

        /// <summary>
        /// Updates the "enabled" state of the "next" and "back" buttons.
        /// </summary>
        /// <param name="prevButtonClickable">True, if the "back" button is to be enabled, false if not.</param>
        /// <param name="nextButtonClickable">True, if the "next" button is to be enabled, false if not.</param>
        public void UpdateButtonState(bool prevButtonClickable, bool nextButtonClickable)
        {
            if (ButtonBack.Dispatcher.Thread != Thread.CurrentThread)
            {
                ButtonBack.Dispatcher.Invoke(new UpdateButtonStateDelegate(UpdateButtonState), prevButtonClickable, nextButtonClickable);
                return;
            }
            ButtonBack.IsEnabled = prevButtonClickable;
            ButtonNext.IsEnabled = nextButtonClickable;
        }
        delegate void UpdateButtonStateDelegate(bool prevButtonClickable, bool nextButtonClickable);


        /// <summary>
        /// Notifies the workflow that a valid processing has been generated.
        /// </summary>
        public void ValidateProcessing()
        {
            this._ValidProcessingExists = true;
        }

        /// <summary>
        /// Notifies the workflow that the current processing's settings have changed and thus no valid processing exists.
        /// </summary>
        public void InvalidateProcessing()
        {
            this._ValidProcessingExists = false;
        }

        /// <summary>
        /// Moves to the next workflow step.
        /// </summary>
        public void NextPage()
        {
            ChangeWorkflowStep(true);
        }

        /// <summary>
        /// Moves to the previous workflow step.
        /// </summary>
        public void PreviousPage()
        {
            ChangeWorkflowStep(false);
        }

        /// <summary>
        /// Closes and shuts down the wizard.
        /// </summary>
        internal void CloseWizard()
        {
            FrameworkElement curElement = HostFrame;
            while (true)
            {
                if (curElement.Parent != null)
                {
                    if (curElement.Parent is FrameworkElement)
                        curElement = curElement.Parent as FrameworkElement;
                    else
                        throw new CxtWizardException("Found unrecognized parent object");
                }
                else
                    break;
            }
            if (curElement is WizardWindow)
                ((WizardWindow)curElement).Close();

        }
    }

    /// <summary>
    /// Base class for wizard internal workflow pages.
    /// </summary>
    internal class InternalConversionWorkflowPage : ConversionWorkflowPage
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        public InternalConversionWorkflowPage()
            : base()
        { }

        /// <summary>
        /// Passes the workflow handle to the page.
        /// </summary>
        /// <param name="workflow">The workflow handle.</param>
        /// <returns>True, if the page is to be displayed, false if not.</returns>
        public virtual bool InternalReceiveWorkflowHandle(ConversionWorkflow workflow)
        {
            return true;
        }
    }
}
